package com.kaibes.core.base.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;

import com.kaibes.core.base.util.BooleanUtils;
import com.kaibes.core.base.util.StringUtils;

public class ClassParserAndroid {

    public static ClassReflection getClassReflectionInject(Class<?> clazz) {
        return getClassReflection(clazz, true);
    }
    
    public static ClassReflection getClassReflection(Class<?> clazz) {
        return getClassReflection(clazz, false);
    }
    
    private static void getAndSetMethod(ClassReflection reflection, String keyName, Field field, Class<?> clazz) {
        try {
            Method getMethod;
            if (BooleanUtils.isBooleanType(field.getType())) {
                getMethod = clazz.getDeclaredMethod("is"+StringUtils.upperCaseFirst(field.getName()));
            } else {
                getMethod = clazz.getDeclaredMethod("get"+StringUtils.upperCaseFirst(field.getName()));
            }
            reflection.getGetMethodMap().put(keyName, getMethod);
            reflection.getSetMethodMap().put(keyName, clazz.getDeclaredMethod("set"+StringUtils.upperCaseFirst(field.getName()), getMethod.getReturnType()));
        } catch (NoSuchMethodException e) {
            if (clazz.getSuperclass() != null) {
                getAndSetMethod(reflection, keyName, field, clazz.getSuperclass());
            } else {
                RuntimeException e1 = new RuntimeException("There is not get/set method for "+field.getName());
                e1.printStackTrace();
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
    
    public static ClassReflection getClassReflection(Class<?> clazz, boolean isInjected) {
        ClassReflection reflection = new ClassReflection(clazz);
        InjectClass annotation = clazz.getAnnotation(InjectClass.class);
        String name = clazz.getName();
        if (annotation != null) {
            name = annotation.value();
        }
        reflection.setName(name);
        ArrayList<Field> fieldsList = getAllFields(clazz);
        StringBuilder nameBuilder = new StringBuilder();
        ArrayList<String> fieldNameList = new ArrayList<>();
        StringBuilder allNameBuilder = new StringBuilder();
        String[] allFieldNameArr = new String[fieldsList.size()];
        int i = 0;
        if (isInjected) {
            String injectName;
            for (Field field : fieldsList) {
                if (field.getAnnotation(NotReflection.class) == null) {
                    name = field.getName();
                    InjectField injectField = field.getAnnotation(InjectField.class);
                    if (injectField != null) {
                        injectName = injectField.value();
                    } else {
                        injectName = name;
                    }
                    
                    if (field.getAnnotation(IgnoreName.class) == null) {
                        nameBuilder.append(injectName).append(",");
                        fieldNameList.add(injectName);
                    }
                    allNameBuilder.append(injectName).append(",");
                    allFieldNameArr[i++] = injectName;
                    reflection.getFieldMap().put(injectName, field);
                    getAndSetMethod(reflection, injectName, field, clazz);
                }
            }
        } else {
            for (Field field : fieldsList) {
                if (field.getAnnotation(NotReflection.class) == null) {
                    name = field.getName();
                    if (field.getAnnotation(IgnoreName.class) == null) {
                        nameBuilder.append(name).append(",");
                        fieldNameList.add(name);
                    }
                    allNameBuilder.append(name).append(",");
                    allFieldNameArr[i++] = name;
                    reflection.getFieldMap().put(name, field);
                    getAndSetMethod(reflection, name, field, clazz);
                }
            }
        }
        String[] fieldNameArr = new String[fieldNameList.size()];
        fieldNameList.toArray(fieldNameArr);
        nameBuilder.deleteCharAt(nameBuilder.length() - 1);
        allNameBuilder.deleteCharAt(allNameBuilder.length() - 1);
        reflection.setAllFieldNameString(allNameBuilder.toString());
        reflection.setAllFieldNameArr(allFieldNameArr);
        reflection.setFieldNameString(nameBuilder.toString());
        reflection.setFieldNameArr(fieldNameArr);
        return reflection;
    }
    
    public static ArrayList<Field> getAllFields(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        ArrayList<Field> fieldsList = new ArrayList<>();
        ArrayList<Field> fieldsListTemp = new ArrayList<>();
        for (Field field : fields) {
            if (field.getAnnotation(NotReflection.class) == null &&
                    !field.getName().equals("$change") && 
                    !field.getName().equals("serialVersionUID")) {
                fieldsList.add(field);
            }
        }
        if (clazz.getSuperclass() != Object.class) {
            fieldsListTemp = getAllFields(clazz.getSuperclass());
        }
        fieldsList.addAll(0, fieldsListTemp);
        fieldsList.trimToSize();
        return fieldsList;
    }
    
}
